added SSCLI 1.0
[windows-sources.git] / shared source / sscli20 / tools / nmake / lexer.cpp
blob4075f425428d951aae7972b51934398a30a233e5
1 // ==++==
2 //
3 //
4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
5 //
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
10 //
11 // You must not remove this notice, or any other, from this software.
12 //
14 // ==--==
15 // LEXER.C -- gets tokens from input, returns them to parse() in parser.c
17 // Purpose:
18 // This module contains the lexical routines of nmake
20 #include "precomp.h"
21 #ifdef _MSC_VER
22 #pragma hdrstop
23 #endif
25 #define COMMENT(A,B,C) (((A) == ';' && B && C) || ((A) == '#'))
26 #ifdef _MBCS
27 #define GET(A) A ? GetTxtChr(file) : lgetc()
28 #else
29 #define GET(A) A ? getc(file) : lgetc()
30 #endif
32 extern char * makeInlineFiles(char*, char**, char**);
33 extern void removeTrailChars(char *);
35 void skipComments(UCHAR);
36 void getString(UCHAR,char*,char*);
37 void getName(char*,char*);
38 UCHAR determineTokenFor(int,char*,char*);
39 void popFileStack(void);
40 UCHAR include(int);
41 char * getPath(const char *);
43 extern const UCHAR nameStates[18][15];
44 extern const UCHAR stringStates[13][14];
45 extern STRINGLIST *targetList;
48 // getToken()
50 // arguments: init global boolean value -- TRUE if tools.ini is the
51 // file being lexed
52 // n size of s[]
53 // expected kind of token expected by parser -- only
54 // needed when parser wants a whole string
55 // (meaning everything left on the current line)
56 // -- this way getToken() doesn't break strings
57 // into their separate tokens
59 // actions: if no tokens have been read from current file,
60 // returns some kind of newline to initialize the parser
61 // (if 1st char in file is whitespace, returns NEWLINESPACE
62 // else returns NEWLINE -- w/o actually getting a token
63 // from the input)
64 // if the parser wants a whole string, reads rest of line
65 // into s and returns STRING
66 // if at end of file, return ACCEPT (which is the last
67 // symbol on the parser's stack)
68 // if input char is newline
69 // if followed by whitespace, return NEWLINESPACE
70 // if the next char is [ and we're reading tools.ini
71 // pretend that we've reached end of file and
72 // return ACCEPT
73 // otherwise return NEWLINE
74 // if input char is colon
75 // if following char is also colon,
76 // (put both chars in s) return DOUBLECOLON
77 // otherwise return SINGLECOLON
78 // if input char is semicolon return SEMICOLON
79 // if input char is equals return EQUALS
80 // if input char is exclamation handle directives
81 // (not yet implemented)
82 // otherwise char must be part of a name, so gather
83 // the rest of the identifier and return NAME
85 // returns: token type: NEWLINE NEWLINESPACE NAME EQUALS COLON
86 // SEMICOLON STRING ACCEPT
88 // modifies: buf by modifying *s, which points somewhere into buf
89 // line global line count
90 // fname will change when !include is handled
91 // colZero global flag set if at column zero of a file
93 // The lexer has to keep track of whether or not it is at the beginning
94 // of a line in the makefile (i.e. in column zero) so that it will know
95 // whether to ignore comments. If init is TRUE, meaning that we are
96 // lexing tools.ini, then we have to treat lines beginning with ';' as
97 // comment lines. If the parser expects a string, only comments beginning
98 // in column zero are ignored; all others are returned as part of the
99 // string. Comments are stripped from macro values (strings that are
100 // part of macro definitions).
102 // The user can specify a macro definition or a build line that
103 // spans several lines (using the <newline> to "continue" the lines) while
104 // interspersing comment lines with the text.
106 UCHAR
107 getToken(
108 unsigned n, // size of s[]
109 UCHAR expected // STRING means get line
110 ) // w/o checking for #;:=
112 char *s;
113 char *end;
114 int c;
116 s = buf;
117 end = buf + n;
118 if (firstToken) { // global var
119 ++line;
120 firstToken = FALSE; // parser needs to see some kind of
121 c = lgetc(); // newline to initialize it
122 if ((colZero = (BOOL) !WHITESPACE(c))) {
123 if (c == EOF)
124 return(determineTokenFor(c,s,end));
125 else
126 UngetTxtChr(c,file);
127 return(NEWLINE);
129 return(NEWLINESPACE);
132 if (expected == STRING || expected == VALUE) { // get everything up to \n
133 getString(expected,s,end);
134 return(expected);
135 } // were/are we
136 c = skipWhiteSpace(FROMLOCAL); // past col 0?
137 *s++ = (char) c; // save the letter
138 *s = '\0'; // terminate s
139 return(determineTokenFor(c,s,end));
143 // determineTokenFor()
145 // arguments: c current input character
146 // s buffer to place token in for return to parser
147 // end end of the token return buffer
149 // returns: token type: NEWLINE NEWLINESPACE NAME EQUALS COLON
150 // SEMICOLON ACCEPT
152 // modifies: buf by modifying *s, which points somewhere into buf
153 // line global line count
154 // fname will change when include is handled
155 // init global flag - set if parsing tools.ini
156 // colZero global flag set if at column zero of a file
158 UCHAR
159 determineTokenFor(
160 int c,
161 char *s,
162 char *end
165 switch (c) {
166 case EOF:
167 if (!feof(file))
168 makeError(line,LEXER+FATAL_ERR);
169 if (incTop)
170 popFileStack();
171 else if (ifTop >= 0) // all directives not processed
172 makeError(line,SYNTAX_EOF_NO_DIRECTIVE);
173 else
174 return(ACCEPT);
176 case '\n':
177 ++line;
178 colZero = TRUE;
179 c = lgetc();
180 if (COMMENT(c,TRUE,init)) {
181 skipComments(FROMLOCAL);
182 ++line;
183 colZero = TRUE;
184 c = lgetc();
186 if ((colZero = (BOOL) !WHITESPACE(c))) {
187 if (c == EOF)
188 return(determineTokenFor(c,s,end));
189 else
190 UngetTxtChr(c,file); //save for next token
191 return(NEWLINE);
193 return(NEWLINESPACE);
195 case ':':
196 colZero = FALSE;
197 if ((c = lgetc()) == ':') {
198 *s++ = (char) c;
199 *s = '\0';
200 return(DOUBLECOLON);
202 UngetTxtChr(c,file);
203 return(COLON);
205 case ';':
206 colZero = FALSE;
207 return(SEMICOLON);
209 case '=':
210 colZero = FALSE;
211 return(EQUALS);
213 case '[':
214 if (init && colZero)
215 return(ACCEPT);
217 case ESCH:
218 UngetTxtChr(c, file); // getName has to get esch
219 s--; // so we don't double the caret
221 default:
222 getName(s,end);
223 if (colZero && !_tcsicmp(buf, "include")) {
224 colZero = FALSE;
225 if ((c = skipWhiteSpace(FROMLOCAL)) != ':'
226 && c != '=') {
227 if (init)
228 makeError(line, SYNTAX_UNEXPECTED_TOKEN, s);
229 return(include(c));
231 UngetTxtChr(c,file);
232 } else
233 colZero = FALSE;
234 return(NAME);
239 // skipWhiteSpace()
241 // arguments: c current input character
242 // init global boolean value -- TRUE if we're lexing tools.ini
243 // colZero global boolean value -- TRUE if the current
244 // input char is at the beginning of the line
246 // actions: reads and discards characters until it gets a
247 // non-whitespace char that isn't part of a comment
248 // or hits the end of the line (NEWLINE and NEWLINESPACE
249 // are valid tokens and shouldn't be skipped w/ whitespace)
250 // backslash-newline ('\\''\n') is treated as whitespace
251 // comments are treated as whitespace
252 // escaped whitespace is treated as whitespace (v1.5)
254 // modifies: colZero global boolean value to :
255 // TRUE if by skipping whitespace and comments we're
256 // at the beginning of a line
257 // else if we skipped characters and are not at the
258 // beginning of a line, FALSE
259 // else if we did not skip any characters, leave
260 // colZero unchanged
262 // returns: c the current non-whitespace input char
265 skipWhiteSpace(
266 UCHAR stream
269 int c;
271 do {
272 c = GET(stream);
273 if (WHITESPACE(c) || c == ESCH) {
274 if (c == ESCH) {
275 c = GET(stream);
276 if (!WHITESPACE(c)) { // push char back out, return esch
277 UngetTxtChr(c, file);
278 c = ESCH;
279 break;
282 colZero = FALSE; // we've moved past col 0
285 if (c == '\\')
286 c = skipBackSlash(c, stream);
287 } while(WHITESPACE(c));
289 if (COMMENT(c,colZero,init)) {
290 skipComments(stream); // current char is always
291 c = '\n'; // \n after comments
292 colZero = TRUE; // always in col 0 after a comment
294 return(c); // true if we're in col 0
298 // ----------------------------------------------------------------------------
299 // skipComments()
301 // arguments: c pointer to current input character
302 // init global boolean value -- TRUE if tools.ini is the
303 // file being lexed
305 // actions: reads and discards characters until it hits the end of
306 // the line
307 // checks to see if 1st char on next line is comment,
308 // and if so, discards that line, too
309 // DO NOT parse backslash-newline. That would break our
310 // precedence of comments over escaped newlines, the reverse
311 // of Xenix.
313 // modifies: line global line count
314 // colZero
316 void
317 skipComments(
318 UCHAR stream
321 int c;
323 for (;;) {
324 colZero = FALSE;
325 do {
326 c = GET(stream);
327 } while (c != EOF && c != '\n');
329 if (c == EOF)
330 return;
331 colZero = TRUE;
332 c = GET(stream);
333 if (!COMMENT(c,TRUE,init)) { // if next line comment,
334 UngetTxtChr(c,file); // go around again
335 return;
337 ++line;
342 // skipBackSlash() - skips backslash-newline sequences
345 // arguments: c current input char
346 // stream flag to determine if chars are to be got
347 // from the raw stream or thru' lgetc()
350 skipBackSlash(
351 int c,
352 UCHAR stream
355 while (c == '\\') { // treat \newline as space
356 if ((c = GET(stream)) == '\n') { // and consume it too
357 colZero = TRUE;
358 ++line; // adjust line count
359 c = GET(stream); // skip over newline
360 if (COMMENT(c,TRUE,init)) { // skip comment line after
361 skipComments(stream); // continuation char
362 ++line;
363 c = GET(stream);
365 } else {
366 UngetTxtChr(c,file);
367 c = '\\';
368 return(c);
371 return(c);
375 // getString()
377 // arguments: type says which kind of token we're getting,
378 // a build STRING, or macro VALUE
379 // (we strip comments from VALUEs, but not
380 // from STRINGs)
381 // s pointer to buffer that will hold string
382 // init global boolean value -- TRUE if tools.ini is the
383 // file being lexed
384 // colZero global boolean value -- true if we 're in
385 // 1st position of line when invoked
386 // end pointer to end of s[]
388 // actions: gets all chars up to the end of line or end of file
389 // and stores them in s[]
390 // backslash followed by newline is replaced by a single
391 // space, and getString() continues getting characters
392 // comments beginning in column 0 are ignored, as are
393 // comments anywhere on a VALUE line
395 // modifies: buf by modifying *s
396 // line global line count
397 // colZero thru' calls to lgetc()
399 // When build strings or macro values are continued on the next line w/
400 // a backslash before the newline, leading whitespace after the newline
401 // is omitted. This is for xmake compatibility.
403 // The continuation character is backslash immediately before newline.
405 // The only difference between build strings and macro values is that
406 // comments are stripped from macro values and not from build strings.
410 void
411 getString(
412 UCHAR type, // build string or macro value?
413 char *s,
414 char *end
417 int c; // buffer
418 UCHAR state;
419 UCHAR input = DEFAULT_;
420 int tempC;
421 unsigned size=0; // whenever state
422 char *begin; // is 0, we're in
423 // column zero
424 BOOL parsechar; // flag to examine char. type
425 BOOL inQuotes = (BOOL) FALSE; // flag when inside quote marks
427 begin = s;
428 c = lgetc();
429 if (type == STRING)
430 state = (UCHAR) 2;
431 else if (WHITESPACE(c)) {
432 state = (UCHAR) 2;
433 c = skipWhiteSpace(FROMLOCAL);
434 } else if (c == ESCH) {
435 c = lgetc();
436 if (WHITESPACE(c)) {
437 state = (UCHAR) 2;
438 c = skipWhiteSpace(FROMLOCAL);
439 } else {
440 UngetTxtChr(c, file);
441 state = (UCHAR) 1; // default state
442 c = ESCH;
444 } else
445 state = (UCHAR) 1; // default state
447 for (;;c = lgetc()) {
448 if (c == '\"')
449 inQuotes = (BOOL) !inQuotes;
450 parsechar = 1; // Default is examine character.
451 if (c == ESCH && !inQuotes && type == VALUE) {
452 c = lgetc();
453 switch (c) {
454 case '$': case ESCH: // Special characters; must
455 case '{': case '}': // not elide esch from string
456 case '(': case ')':
457 case '!': case '-': case '@':
458 *s++ = ESCH;
459 if (s == end) {
460 if (string == NULL) { // Increase size of s
461 string = (char *) allocate(MAXBUF<<1);
462 _tcsncpy(string,begin,MAXBUF);
463 s = string + MAXBUF;
464 size = MAXBUF << 1;
465 end = string + size;
466 } else {
467 if ((size + MAXBUF < size) // overflow error
468 || !(string = (char *) REALLOC(string,size+MAXBUF)))
469 makeError(line, MACRO_TOO_LONG);
470 s = string + size;
471 size += MAXBUF;
472 end = string + size;
474 begin = string;
476 case '#': case '\n': // elide esch right now!
477 case '\\': case '\"':
478 input = DEFAULT_;
479 parsechar = 0; // DON'T examine character
480 break;
481 default:
482 break; // DO examine character.
484 } else if (c == ESCH) {
485 c = lgetc();
486 UngetTxtChr(c, file);
487 c = ESCH;
490 if (parsechar) {
491 switch (c) {
492 case '#': input = COMMENT_; break;
493 case '=': input = EQUALS_; break;
494 case ':': input = COLON_; break;
495 case '$': input = DOLLAR_; break;
496 case '(': input = OPENPAREN_; break;
497 case ')': input = CLOSEPAREN_; break;
498 case '\\': input = BACKSLASH_; break;
499 case '\n':
500 case EOF: input = NEWLINE_; break;
501 case ' ':
502 case '\t': input = WHITESPACE_; break;
503 case '*': input = STAR_; break;
504 case '@':
505 case '<':
506 case '?': input = SPECIAL1_; break;
507 case 'F':
508 case 'D':
509 case 'B':
510 case 'R': input = SPECIAL2_; break;
511 case ';': input = (UCHAR) (!state && init ? COMMENT_ : DEFAULT_);
512 break; /* Handle comments in tools.ini */
514 default: input = (UCHAR) (MACRO_CHAR(c) ? MACROCHAR_:DEFAULT_);
515 break;
518 if (input == SPECIAL1_ && type == STRING && c == '<') {
519 if ((tempC = lgetc()) == '<') { // << means start
520 s = makeInlineFiles(s, &begin, &end); // an inline file
521 input = NEWLINE_;
522 c = '\n'; line--; // adding a '\n', we need to remove a line to compensate
523 } else {
524 UngetTxtChr(tempC,file);
526 state = stringStates[state][input];
527 } else if (input == COMMENT_) { // Handle comments
528 if (!state) {
529 inQuotes = (BOOL) FALSE;
530 skipComments(FROMLOCAL);
531 ++line;
532 continue;
534 else if (type == VALUE)
535 state = OK; // don't elide from command
536 else
537 state = stringStates[state][input];
538 } else
539 state = stringStates[state][input];
541 if (state == OK) { // Accept end of string
542 inQuotes = (BOOL) FALSE;
543 UngetTxtChr(c,file);
545 // Strip trailing whitespace from string. Easier to do it here,
546 // else we have to treat a multi-string value (OBJS=a b c) as
547 // separate tokens.
549 while (s > begin && _istspace(s[-1]))
550 --s;
551 *s = '\0';
552 if (string) {
553 if ((s = (char *) REALLOC(string, (size_t) (s - string + 1))))
554 string = s;
555 } else
556 string = makeString(begin);
557 return;
558 } else if (ON(state,ERROR_MASK)) // Error code from table
559 makeError(line,(state&~ERROR_MASK)+FATAL_ERR,c);
561 if (!state) { // Col 0; we just hit \nl
562 *--s = ' '; // so treat it like white-
563 ++s; ++line; // space; overwrite the
564 colZero = TRUE; // backslash with a space.
565 c = lgetc();
566 colZero = FALSE;
567 if (WHITESPACE(c)) {
568 state = 2;
569 do {
570 c = lgetc();
571 } while (WHITESPACE(c));
573 UngetTxtChr(c,file);
574 } else { // Keep storing string
575 *s++ = (char) c;
576 if (s == end) {
577 if (!string) { // Increase size of s
578 string = (char *) allocate(MAXBUF<<1);
579 _tcsncpy(string,begin,MAXBUF);
580 s = string + MAXBUF;
581 size = MAXBUF << 1;
582 end = string + size;
583 } else {
584 if ((size + MAXBUF < size) // overflow error
585 || !(string = (char *) REALLOC(string,size+MAXBUF)))
586 makeError(line, MACRO_TOO_LONG);
587 s = string + size;
588 size += MAXBUF;
589 end = string + size;
597 // getName()
599 // arguments: s pointer into buffer that will hold string
600 // (s is pointing to buf+1 when passed, because
601 // the caller, getToken(), has already seen and
602 // saved one char)
603 // init global boolean value -- TRUE if tools.ini is the
604 // file being lexed
605 // used by routine called - lgetc()
606 // end pointer to end of s[]
608 // actions: gets all chars up to first token delimiter and stores
609 // them in s[] (delimiters are ' ', '\t', '\n' and (when
610 // not inside a macro invocation) ':' and '='
611 // note that backslash-newline is treated as a space,
612 // which is a delimiter
613 // if the current input char is '$' this must be a macro
614 // invocation
615 // if the macro name is in parentheses
616 // get all chars up to and including close paren
617 // (if ')' not found, error)
619 // We check the syntax within the name here -- thus errors in macro
620 // invocation syntax will be caught. Special macros cannot be used
621 // as part of names, with the exception of the dynamic dependency macros.
623 // We can probably never overrun our buffer, because it would be extremely
624 // difficult for the user to get a name with 1024 characters or more into
625 // his makefile.
627 // we never end up in column zero, because we push the delimiter back
628 // out on the input
630 // uses state table defined in table.h, defs from grammar.h
632 // modifies: line (possibly) thru' call to lgetc()
633 // file (possibly) if lgetc() finds a !include
634 // fName (possibly) if lgetc() finds a !include
636 void
637 getName(
638 char *s,
639 char *end // pts to end of s
642 int c;
643 UCHAR state;
644 UCHAR input=DEFAULT_;
645 BOOL seenBackSlash = FALSE;
646 BOOL fQuoted = FALSE;
647 char *beg = s - 1;
648 BOOL parsechar; // flag to examine char. type
650 switch (*(s-1)) {
651 case '$': state = (UCHAR) 2; break;
652 case '{': state = (UCHAR) 8; break;
653 case '"': fQuoted = TRUE; state = (UCHAR)16; break;
654 default: state = (UCHAR) 0; break;
657 for (;;) {
658 c = lgetc();
659 parsechar = 1; // Default is examine char.
660 if (c == ESCH) {
661 c = lgetc();
662 switch (c) {
663 case '{': // Special characters; must
664 case '}': // not elide esch from string
665 case '(':
666 case ')':
667 case '$':
668 case ESCH:
669 *s++ = ESCH;
671 case '#': // elide esch right now!
672 case '\n':
673 case '\\':
674 input = DEFAULT_;
675 parsechar = 0; // DON'T examine character
676 break;
677 default:
678 break; // DO examine character.
681 if (parsechar) {
682 switch (c) {
683 case '#' : input = COMMENT_; break;
684 case '=' : input = EQUALS_; break;
685 case ';' : input = SEMICOLON_; break;
686 case ':' : input = COLON_; break;
687 case '$' : input = DOLLAR_; break;
688 case '(' : input = OPENPAREN_; break;
689 case ')' : input = CLOSEPAREN_; break;
690 case '{' : input = OPENCURLY_; break;
691 case '}' : input = CLOSECURLY_; break;
692 case ' ' :
693 case '\t': input = (UCHAR)((fQuoted)
694 ? DEFAULT_ : WHITESPACE_);
695 break;
696 case '\n':
697 case EOF : input = NEWLINE_; break;
698 case '\\': input = BKSLSH_; break;
699 case '"' : input = QUOTE_;
700 if (state == 18) {
701 // found a quote after a path list {...}
702 // handle as quoted name
703 fQuoted = 1;
705 break;
707 // Add support for $* and $@ on the dependency line
708 default :
709 if (ON(actionFlags, A_DEPENDENT))
710 input = (UCHAR)((MACRO_CHAR(c) || c == '*' || c == '@')
711 ?MACROCHAR_:DEFAULT_);
712 else
713 input = (UCHAR)(MACRO_CHAR(c)?MACROCHAR_:DEFAULT_);
714 break;
717 state = nameStates[state][input];
719 // Cheat lex table to think that you are handling quoted string case
721 if (fQuoted && state == 1)
722 state = 16;
724 // seenBackSlash is used to provide lookahead when \ is seen on a
725 // dependency line
726 if (seenBackSlash)
727 // if \ followed by \n then use it as a continuation
728 if (input == NEWLINE_) {
729 ++line;
730 colZero = TRUE;
731 c = lgetc();
732 colZero = FALSE;
733 if (WHITESPACE(c)) {
734 state = OK;
735 do {
736 c = lgetc();
737 } while (WHITESPACE(c));
738 } else
739 state = (UCHAR)((s == buf + 1) ? BEG : DEF);
740 } else
741 *s++ = '\\';
742 seenBackSlash = FALSE;
743 if (s >= end)
744 makeError(line,NAME_TOO_LONG);
745 if (state == OK) {
746 UngetTxtChr(c,file);
747 *s = '\0';
748 removeTrailChars(beg);
749 return;
750 } else if (ON(state,ERROR_MASK))
751 makeError(line,(state&~ERROR_MASK)+FATAL_ERR,c);
753 if (state == BKS) {
754 seenBackSlash = TRUE; //set lookahead flag
755 } else
756 *s++ = (char) c;
761 // createDosTmp -- Creates a unique temporary file.
763 // Scope:
764 // Global.
766 // Purpose:
767 // To create a unique temporary file by calling _mktemp() but it gets
768 // over _mktemp() limitation to be able to create more files.
770 // Input:
771 // path -- The buffer initially contain the directory to store the temp
772 // file. On exit, if success, the temp file is appended to it.
773 // In case of failure, the its contents is undetermined.
775 // Output:
776 // If successful, temporary file name is appended to path and
777 // the function returns the file pointer, else NULL.
779 FILE *
780 createDosTmp(
781 char *path
784 FILE *fd = NULL;
787 char szDir[_MAX_PATH];
790 if (!path || !*path) { // If path is empty, use "."
791 _tcscpy(szDir, ".");
792 } else {
793 _tcscpy(szDir, path);
798 // Use GetTempFileName to overcome limitations of _mktemp
799 // regarding the max number of generated files
800 char szTempFile[_MAX_PATH];
801 if (GetTempFileName (path, "nm", 0, szTempFile)) {
802 _tcscpy(path, szTempFile);
803 // Open the file and return the file's descriptor.
804 fd = FILEOPEN(path, "w");
807 return fd;
811 void
812 popFileStack()
814 if (fclose(file) == EOF)
815 makeError(0, ERROR_CLOSING_FILE, fName);
816 FREE(fName);
817 file = incStack[--incTop].file;
818 fName = incStack[incTop].name;
819 line = incStack[incTop].line;
823 // include() -- handle include files
825 // arguments: c first non-whitespace char after the string
826 // INCLUDE on the line...
827 // colZero global boolean value, set if currently at
828 // column zero of a file.
830 // modifies: line global line count - if include file opened
831 // file global pointer to current file
832 // fName global pointer to name of current file
833 // colZero global boolean value, changed if include
834 // file opened and char from colZero is returned
836 UCHAR
837 include(
838 int c
841 size_t n;
842 char *s;
844 if (c == '\n' || c == EOF)
845 makeError(line,SYNTAX_NO_NAME);
847 *buf = (char) c;
848 if (!fgets(buf+1,MAXBUF - 1,file)) {
849 if (feof(file))
850 makeError(line,SYNTAX_UNEXPECTED_TOKEN,"EOF");
851 makeError(line,CANT_READ_FILE);
853 n = _tcslen(buf) - 1;
854 if (buf[n] == '\n') {
855 buf[n] = '\0';
857 s = buf;
858 while (WHITESPACE(*s))
859 ++s;
860 return(processIncludeFile(s));
864 // processIncludeFile() -- checks for include file and switches state
866 // arguments: s buffer that has include file name
867 // colZero global boolean value, set if currently at
868 // column zero of a file.
869 // init global boolean - set if tools.ini is being lexed
870 // used by lgetc() which is called from here...
872 // modifies: line global line count - if include file opened
873 // file global pointer to current file
874 // fName global pointer to name of current file
875 // colZero global boolean value, changed if include
876 // file opened and char from colZero is returned
878 UCHAR
879 processIncludeFile(
880 char *s
883 MACRODEF *m;
884 struct _finddata_t finddata;
885 NMHANDLE searchHandle;
886 char *t, *p, *u;
887 int c = 0;
888 int i;
890 if (!*s || *s == '#') {
891 makeError(line, SYNTAX_NO_NAME);
894 if ((t = _tcspbrk(s,"\t#"))) {
895 if (*t == '#') {
896 c = *t;
899 *t = '\0';
901 if (!c) {
902 for (u = t; *++u;) { // check for extra
903 if (*u == '#') {
904 break; // text on line
907 if (!WHITESPACE(*u)) {
908 makeError(line, SYNTAX_UNEXPECTED_TOKEN, u);
912 } else {
913 t = s + _tcslen(s);
916 // remove trailing white space
917 while (t > s) {
918 char *prev;
919 prev = _tcsdec(s, t);
920 if (!WHITESPACE(*prev))
921 break;
922 t = prev;
924 *t = '\0';
926 if (*s == '<' && *(t-1) == '>') {
927 char *pt;
929 *--t = '\0';
930 p = removeMacros(++s);
931 p = p == s ? makeString(s) : p;
932 t = (m = findMacro("INCLUDE")) ? m->values->text : (char*) NULL;
933 if (t != NULL) { // expand INCLUDE macro before passing it on
934 char * pt1;
936 pt1= makeString(t);
937 pt = removeMacros(pt1);
938 if (pt != pt1) {
939 FREE(pt1); // we've got a new string, free old one
941 } else {
942 pt = NULL;
945 if (!(u = searchPath(pt, p, &finddata, &searchHandle))) {
946 makeError(line, CANT_OPEN_FILE, p);
949 if (pt) {
950 FREE(pt);
953 FREE(p);
954 s = u;
955 } else {
956 if (*s == '"' && *(t-1) == '"') {
957 *--t = '\0';
958 ++s;
960 p = removeMacros(s);
961 p = p == s ? makeString(s) : p;
962 if (!findFirst(p, &finddata, &searchHandle)) {
963 if (!_tcspbrk(p, "\\/:")) {
964 //use C sematics for include
965 for (i = incTop;i >= 0;i--) {
966 t = (i == incTop) ? fName : incStack[i].name;
967 if (!(t = getPath(t)))
968 continue;
969 u = (char *)allocate(_tcslen(t) + 1 + _tcslen(p) + 1);
970 _tcscat(_tcscat(_tcscpy(u, t), PATH_SEPARATOR), p);
971 if (findFirst(u, &finddata, &searchHandle)) {
972 s = u;
973 FREE(t);
974 break;
976 FREE(t);
977 FREE(u);
979 FREE(p);
980 if (i < 0) {
981 makeError(line, CANT_OPEN_FILE, s);
983 } else {
984 makeError(line, CANT_OPEN_FILE, p);
989 for (i = 0; i < incTop; ++i) { // test for cycles
990 if (!_tcsicmp(s,incStack[i].name)) {
991 makeError(line, CYCLE_IN_INCLUDES, s);
995 incStack[incTop].file = file; // push info on stack
996 incStack[incTop].line = line;
997 incStack[incTop++].name = fName;
998 currentLine = 0;
1000 if (!(file = OpenValidateMakefile(s,"rt"))) { // read, text mode
1001 makeError(line,CANT_OPEN_FILE,s);
1004 fName = makeString(s);
1005 line = 1;
1006 colZero = TRUE; // parser needs to see some kind of
1007 c = lgetc(); // newline to initialize it for this
1009 if ((colZero = (BOOL) !WHITESPACE(c))) { // file
1010 UngetTxtChr(c,file);
1011 line=0; // We did not start reading the file
1012 return(NEWLINE);
1015 return(NEWLINESPACE);
1019 // getPath -- return the drive/directory parts of a full path
1021 // Scope:
1022 // Local
1024 // Purpose:
1025 // This function returns the drive/directory parts of a full path. Space is
1026 // allocated for the resulting string, so the caller is responsible for freeing
1027 // it after use.
1029 // Input: pszFullPath -- The full pathname.
1031 // Assumes: Pathnames use MS-DOS file naming convension.
1033 // Notes:
1034 // To allocate temporary memory for the drive and path components, I have used
1035 // _MAX_DRIVE and _MAX_DIR. Under Windows NT there are two possibilities:
1036 // 1. These two parameters can be so large that the stack will be overflow
1037 // 2. They are not large enough (?)
1039 char *
1040 getPath(
1041 const char *pszFullPath
1044 char szDrive[_MAX_DRIVE];
1045 char szDir[_MAX_DIR];
1046 char *szPath;
1047 char *pszSlash;
1049 // Separate the components of the fullpath
1050 _splitpath(pszFullPath, szDrive, szDir, NULL, NULL);
1052 // Allocate just enough memory to hold the drive/path combo then
1053 // Glue just the drive and dir component back together.
1054 szPath = (char *) rallocate(_tcslen(szDrive) + _tcslen(szDir) + 1);
1055 _makepath(szPath, szDrive, szDir, NULL, NULL);
1057 // Eliminate the trailing slash/blackslash to retain compatibility with
1058 // the older version of getPath()
1059 pszSlash = szPath + _tcslen(szPath) - 1;
1060 if (IsPathSeparator(*pszSlash)) {
1061 *pszSlash = '\0';
1064 return szPath;